<?php
// +----------------------------------------------------------------------
// | flow-notify / UniPush.php    [ 2021/11/9 1:30 下午 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2021 zhangqiquan All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangqiquan <768617998@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);


namespace notify;

// 【使用队列执行，多线程下可能会出现token失效的问题】
use notify\unipush\Report;
use notify\unipush\User;

class UniPush
{
    use Report;
    use User;
    protected $config = [
        'app_id' => '',
        'app_key' => '',
        'app_secret' => '',
        'master_secret' => '',
    ];

    protected $taskid = null;

    /**
     * 构造方法
     * UniPush constructor.
     * @param array $config
     */
    public function __construct(array $config = []){
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 推送给单个用户
     * @param string $cid
     * @param string $title
     * @param string $body
     * @param array $loadData
     * @param bool $isAsync
     * @return bool|mixed|string
     * @throws \Exception
     */
    public function pushSingle(string $cid, string $title, string $body, array $loadData = ['body' => '推送消息给单个用户'], bool $isAsync = true){
        $notification = $this->getNotification($title, $body, $loadData);
        $notification['audience']['cid'] = [$cid];
        $header = [
            'Content-Type: application/json',
            'token: '.$this->getToken()
        ];
        return $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/push/single/cid', $notification, $header);
    }

    /**
     * 单个消息批量推送数据
     * @var array
     */
    protected $batchSingle = [];

    /**
     * 构造单个消息批量推送
     * @param string $cid
     * @param string $title
     * @param string $body
     * @param array $loadData
     * @return $this
     */
    public function pushAddSingle(string $cid, string $title, string $body, array $loadData = ['body' => '批量推送']){
        $notification = $this->getNotification($title, $body, $loadData);
        $notification['audience']['cid'] = [$cid];
        $this->batchSingle[] = $notification;
        return $this;
    }

    /**
     * 执行单个消息批量推送
     * @param bool $isAsync
     * @return bool|mixed|string
     * @throws \Exception
     */
    public function pushBatchSingleSend(bool $isAsync = true){
        if(!$this->batchSingle) throw new \Exception("batchSingle不能为空", 500);

        $header = [
            'Content-Type: application/json',
            'token: '.$this->getToken()
        ];

        $requestData = [
            'is_async' => $isAsync,
            'msg_list' => $this->batchSingle
        ];
        return $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/push/single/batch/cid', $requestData, $header);
    }

    /**
     * 使用cid批量推送【同一消息】
     * @param array $cid uniapp那边限制，cid数组，数组长度不大于1000
     * @param string $title
     * @param string $body
     * @param array $loadData
     * @param bool $isAsync
     * @throws \Exception
     */
    public function pushLists(array $cid, string $title, string $body, array $loadData = ['' => '批量推送cid'], bool $isAsync = true){
        $taskid = $this->createMessage($title, $body, $loadData);
        $requestData = [
            "audience" => [
                "cid" => $cid,
            ],
            "taskid" => $taskid,
            "is_async" => $isAsync
        ];
        $header = [
            'Content-Type: application/json',
            'token: '.$this->getToken()
        ];
        return $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/push/list/cid', $requestData, $header);
    }

    // 推送给所有用户

    /**
     * 推送给所有用户消息
     * @param string $title
     * @param string $body
     * @param array $loadData
     * @throws \Exception
     */
    public function pushAll(string $title, string $body, array $loadData = []){
        $requestData = $this->getNotification($title, $body, $loadData);
        $header = [
            'Content-Type: application/json',
            'token: '.$this->getToken()
        ];
        return $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/push/all', $requestData, $header);
    }

    /**
     * 创建批量用户发送消息id
     * @param $title
     * @param $body
     * @param $loadData
     * @return mixed|string
     * @throws \Exception
     */
    protected function createMessage($title, $body, $loadData){
        if(!$this->taskid){
            $requestData = $this->getNotification($title, $body, $loadData);
            $header = [
                'Content-Type: application/json',
                'token: '.$this->getToken()
            ];
            $res = $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/push/list/message', $requestData, $header);
            $this->taskid = $res['data']['taskid'];
        }
        return $this->taskid;
    }

    /**
     * 获取请求id
     * @return string
     */
    protected function getRequestId(){
        return md5(($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1').microtime(true).rand(10000, 99999));
    }

    /**
     * 获取消息数据
     * @param string $title
     * @param string $body
     * @param array $loadData
     * @param string $clickType startapp离线的时候没有问题|在线的时候无法跳转，payload直接报错
     * @return array
     */
    protected function getNotification(string $title, string $body, array $loadData = [], $clickType = 'payload'){

        $requestId = $this->getRequestId();

        $notification = [
            "title" => $title,
            "body" => $body,
            "click_type" => $clickType,
            'payload' => json_encode($loadData),
        ];

        $notification = array_merge($notification, $loadData);

        $data = [
            "request_id" => $requestId,
            "group_name" => $requestId,
            "settings" => [
                "ttl" => 3600000*10,
                "strategy" => [
                    'default' => 1,
                    'st' => 1,
                    "ios" => 2,
                ]
            ],
            "push_message" => [
                "notification" => $notification

            ],
            "push_channel" => [
                "ios" => [
                    "type" => "notify",
                    "aps" => [
                        "alert" => $notification,
                    ]
                ],
                "android" => [
                    "ups" => [
                        "notification" => [
                            "title" => $title,
                            "body" => $body,
                            "click_type" => 'intent',
                            'intent' =>  'intent:#Intent;action=android.intent.action.oppopush;launchFlags=0x04000000;component=cn.flownow.www/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title='.$title.';S.content='.$body.';S.payload='.json_encode($loadData).';end'
                        ],
                        // 额外参数 可以区分机型 定义特殊属性
                        //"options" => [ "ALL" => [ "channel" => "default"]]],
                    ],
                ]
            ]
        ];

        return $data;
    }
    /**
     * 获取token
     * @return mixed|string
     * @throws \Exception
     */
    protected function getToken(){

        $tokenFile = Notify::rootPath().DIRECTORY_SEPARATOR.'unipush-token.txt';
        $token = '';
        if(file_exists($tokenFile)) $token = file_get_contents($tokenFile);
        if($token){
            $tokens = explode('###', $token);
            if(time() - $tokens[1] <= 3600){
                return $tokens[0];
            }
        }

        $timestamp = time()*1000;
        $sign = hash("sha256", $this->config['app_key'].$timestamp.$this->config['master_secret']);

        // 7200秒过期
        $curlData = $this->curlRequest('https://restapi.getui.com/v2/'.$this->config['app_id'].'/auth', [
            'sign' => $sign,
            'timestamp' => $timestamp,
            'appkey' => $this->config['app_key'],
        ], [
            'Content-Type: application/json',
        ]);
        $token = $curlData['data']['token'];
        file_put_contents($tokenFile, $token.'###'.time());

        return $token;
    }

    /**
     * 执行请求
     * @param string $url
     * @param array $data
     * @return bool|mixed|string
     * @throws \Exception
     */
    protected function curlRequest(string $url, array $data, array $header = [], bool $isPost = true){
        $postFields = json_encode($data);
        $ch = curl_init ();
        curl_setopt( $ch, CURLOPT_URL, $url );
        if($header) curl_setopt( $ch, CURLOPT_HTTPHEADER, $header); // 请求头参数
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); //若果报错 name lookup timed out 报错时添加这一行代码
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );

        if($isPost){
            curl_setopt( $ch, CURLOPT_POST, 1 );
            curl_setopt( $ch, CURLOPT_POSTFIELDS, $postFields);
        }

        curl_setopt( $ch, CURLOPT_TIMEOUT,10);
        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0);
        $ret = curl_exec ( $ch );
        if (false == $ret) {
            throw new \Exception(curl_error(  $ch), 500);
        } else {
            $rsp = curl_getinfo( $ch, CURLINFO_HTTP_CODE);
            if (200 != $rsp) {
                throw new \Exception($url.';请求:'.$postFields.';错误:'.$rsp.';结果:'.$ret, 500);
            } else {
                $result = $ret;
            }
        }
        curl_close ( $ch );
        $output = json_decode($result, true);
        if($output) $result = $output;
        return $result;
    }
}